<!DOCTYPE html>


<html lang="zh-Hans">


<head>
  <meta charset="utf-8" />
   
  <meta name="keywords" content="代码,生活,杂记,思考" />
   
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    http |  AURORA
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

<link rel="alternate" href="/atom.xml" title="AURORA" type="application/atom+xml">
</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-http"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  http
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/02/14/http/" class="article-date">
  <time datetime="2020-02-13T16:00:00.000Z" itemprop="datePublished">2020-02-14 00:02:00</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E6%8A%80%E6%9C%AF/">技术</a> / <a class="article-category-link" href="/categories/%E6%8A%80%E6%9C%AF/%E8%AE%A1%E7%AE%97%E6%9C%BA%E9%80%9A%E8%AF%86/">计算机通识</a> / <a class="article-category-link" href="/categories/%E6%8A%80%E6%9C%AF/%E8%AE%A1%E7%AE%97%E6%9C%BA%E9%80%9A%E8%AF%86/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/">计算机网络</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> Word count:</span>
            <span class="post-count">7.7k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> Reading time≈</span>
            <span class="post-count">27 min</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="Http简述"><a href="#Http简述" class="headerlink" title="Http简述"></a>Http简述</h1><p>HTTP是一个基于TCP/IP通信协议来传递数据（HTML 文件, 图片文件, 查询结果等）。HTTP是一个属于应用层的面向对象的协议，由于其简捷、快速的方式，适用于分布式超媒体信息系统。它于1990年提出，经过几年的使用与发展，得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版，HTTP/1.1的规范化工作正在进行之中，而且HTTP-NG(Next Generation of HTTP)的建议已经提出。<strong>HTTP协议工作于客户端-服务端架构为上</strong>。<strong>浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后，向客户端发送响应信息</strong>。</p>
<a id="more"></a>
<h1 id="Http版本"><a href="#Http版本" class="headerlink" title="Http版本"></a>Http版本</h1><h2 id="http0-9"><a href="#http0-9" class="headerlink" title="http0.9"></a>http0.9</h2><p>http于1990年问世，那时候并没有作为正式的标准被建立，因此称为HTTP/0.9</p>
<h2 id="http1-0"><a href="#http1-0" class="headerlink" title="http1.0"></a>http1.0</h2><p>1996年http正式作为标准被公布。该标准如今都被广泛用于服务端<br>缺陷：</p>
<ol>
<li>无法复用连接，完成即断开，重新慢启动和 TCP 3次握手</li>
<li>head of line blocking: 线头阻塞，导致请求之间互相影响</li>
</ol>
<h2 id="http-1-1"><a href="#http-1-1" class="headerlink" title="http 1.1"></a>http 1.1</h2><p>1997年公布的http1.1是如今主流的http主流协议版本<br>较1.0的特点：</p>
<ol>
<li>长连接：HTTP 1.1支持长连接（PersistentConnection）和请求的流水线（Pipelining）处理，在一个TCP连接上可以传送多个HTTP请求和响应，Connection： keep-alive，一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。</li>
<li>缓存处理：在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准，HTTP1.1则引入了更多的缓存控制策略例如Entity tag，If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。</li>
<li>带宽优化及网络连接的使用：HTTP1.0中，存在一些浪费带宽的现象，例如客户端只是需要某个对象的一部分，而服务器却将整个对象送过来了，并且不支持断点续传功能，HTTP1.1则在请求头引入了range头域，它允许只请求资源的某个部分，即返回码是206（Partial Content），这样就方便了开发者自由的选择以便于充分利用带宽和连接。</li>
<li>Host头处理：在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址，因此，请求消息中的URL并没有传递主机名（hostname）。但随着虚拟主机技术的发展，在一台物理服务器上可以存在多个虚拟主机（Multi-homed Web Servers），并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域，且请求消息中如果没有Host头域会报告一个错误（400 Bad Request）。</li>
</ol>
<h2 id="http-2-0"><a href="#http-2-0" class="headerlink" title="http 2.0"></a>http 2.0</h2><h3 id="http1-1的缺点"><a href="#http1-1的缺点" class="headerlink" title="http1.1的缺点"></a>http1.1的缺点</h3><ol>
<li>HTTP/1.1使用的流水线技术也只能部分处理请求并发，仍然会存在队列头阻塞问题（线头阻塞），因此客户端在需要发起多次请求时，通常会采用建立多连接来减少延迟。</li>
<li>单向请求，只能由客户端发起。</li>
<li>请求报文与响应报文首部信息冗余量大。</li>
<li>数据未压缩，导致数据的传输量大。</li>
</ol>
<h3 id="http-2-0的特点"><a href="#http-2-0的特点" class="headerlink" title="http 2.0的特点"></a>http 2.0的特点</h3><h4 id="二进制传输"><a href="#二进制传输" class="headerlink" title="二进制传输"></a>二进制传输</h4><p>在HTTP1.x中，我们是通过文本的方式传输数据。基于文本的方式传输数据存在很多缺陷，文本的表现形式有多样性，因此要做到健壮性考虑的场景必然有很多，但是二进制则不同，只有0和1的组合，因此选择了二进制传输，实现方便且健壮。<br>在HTTP2.0中引入了新的编码机制，所有传输的数据都会被分割，并采用二进制格式编码。</p>
<h4 id="多路复用"><a href="#多路复用" class="headerlink" title="多路复用"></a>多路复用</h4><p>在一个TCP连接中存在多个流，即可以同时发送多个请求，对端可以通过帧中的表示知道该帧属于哪个请求。在客户端，这些帧乱序发送，到对端后再根据每个帧首部的流标识符重新组装。通过该技术，可以避免HTTP旧版本的队头阻塞问题，极大提高传输性能。</p>
<h4 id="header压缩"><a href="#header压缩" class="headerlink" title="header压缩"></a>header压缩</h4><p>在HTTP1.0中，我们使用文本的形式传输header，在header中携带cookie的话，每次都需要重复传输几百到几千的字节，这着实是一笔不小的开销。<br>在HTTP2.0中，我们使用了HPACK（HTTP2头部压缩算法）压缩格式对传输的header进行编码，减少了header的大小。并在两端维护了索引表，用于记录出现过的header，后面在传输过程中就可以传输已经记录过的header的键名，对端收到数据后就可以通过键名找到对应的值。</p>
<h4 id="服务器push"><a href="#服务器push" class="headerlink" title="服务器push"></a>服务器push</h4><p>在HTTP2.0中，服务端可以在客户端某个请求后，主动推送其他资源。<br>可以想象一下，某些资源客户端是一定会请求的，这时就可以采取服务端push的技术，提前给客户端推送必要的资源，就可以相对减少一点延迟时间。在浏览器兼容的情况下也可以使用prefetch。</p>
<h4 id="更安全"><a href="#更安全" class="headerlink" title="更安全"></a>更安全</h4><p>HTTP2.0使用了tls的拓展ALPN做为协议升级，除此之外，HTTP2.0对tls的安全性做了近一步加强，通过黑名单机制禁用了几百种不再安全的加密算法。</p>
<h1 id="URI和URL"><a href="#URI和URL" class="headerlink" title="URI和URL"></a>URI和URL</h1><p>各自的概念</p>
<ul>
<li>URI：统一资源标识符</li>
<li>URL：统一资源定位符</li>
</ul>
<p>URI用字符串标识某一互联网资源，而URL则表示资源的地点（互联网上所处的位置）。URL是URI的子集</p>
<h1 id="Http报文结构与内容"><a href="#Http报文结构与内容" class="headerlink" title="Http报文结构与内容"></a>Http报文结构与内容</h1><p>http报文本身是由多行（CRLF作为换行符）数据构成的字符串文本。http报文主要分文两大部分：<strong>报文首部和报文主体</strong>，两者之间有一个CRLF分割。报文首部有服务端或客户端需处理的请求或响应的内容及属性。报文主体是发送的数据</p>
<h2 id="http请求报文"><a href="#http请求报文" class="headerlink" title="http请求报文"></a>http请求报文</h2><p><img src="https://img-blog.csdnimg.cn/2020021321000062.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<ol>
<li>报文分为<strong>报文首部和报文主体</strong></li>
<li>报文首部的第一行称为<strong>请求行</strong>，包括：请求方法、请求URI，http版本</li>
<li>首行之后是各种<strong>请求头</strong>，含有：通用头部，请求头部，实体头部（HTTP首部）</li>
<li>报文首部和报文主体之间有一个<strong>CRLF</strong>分隔</li>
</ol>
<h2 id="http响应报文"><a href="#http响应报文" class="headerlink" title="http响应报文"></a>http响应报文</h2><p><img src="https://img-blog.csdnimg.cn/20200213210247319.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<ol>
<li>报文分为<strong>报文首部和报文主体</strong></li>
<li>报文首部的第一行称为<strong>状态行</strong>，包括：http版本、状态码、状态说明</li>
<li>首行之后是若干行<strong>响应头</strong>，包括：通用头部，响应头部，实体头部（HTTP首部）</li>
<li>报文首部和报文主体之间有一个<strong>CRLF</strong>分隔</li>
</ol>
<h2 id="http首部字段：请求头和响应头"><a href="#http首部字段：请求头和响应头" class="headerlink" title="http首部字段：请求头和响应头"></a>http首部字段：请求头和响应头</h2><p>http首部字段由首部字段名和字段值组成，中间用冒号分割</p>
<blockquote>
<p>首部字段名：字段值</p>
</blockquote>
<p>例如：Content-Type: text/html</p>
<p>另外，一个首部字段名可以对应多个值</p>
<p>例如：Keep-Alive:timeout=15,max=100</p>
<h3 id="四种HTTP首部字段类型"><a href="#四种HTTP首部字段类型" class="headerlink" title="四种HTTP首部字段类型"></a>四种HTTP首部字段类型</h3><ol>
<li>通用首部字段<br>请求和响应都会用到的字段</li>
<li>请求首部字段<br>请求报文时使用的首部</li>
<li>响应首部字段<br>响应报文时使用的首部</li>
<li>实体首部字段<br>针对报文的实体部分使用的首部</li>
</ol>
<h3 id="通用首部字段"><a href="#通用首部字段" class="headerlink" title="通用首部字段"></a>通用首部字段</h3><p><img src="https://img-blog.csdnimg.cn/20200213221634627.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h3 id="请求首部字段"><a href="#请求首部字段" class="headerlink" title="请求首部字段"></a>请求首部字段</h3><p><img src="https://img-blog.csdnimg.cn/20200213221710807.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h3 id="响应首部字段"><a href="#响应首部字段" class="headerlink" title="响应首部字段"></a>响应首部字段</h3><p><img src="https://img-blog.csdnimg.cn/20200213221723744.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h3 id="实体首部字段"><a href="#实体首部字段" class="headerlink" title="实体首部字段"></a>实体首部字段</h3><p><img src="https://img-blog.csdnimg.cn/20200213221737410.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h3 id="非HTTP1-1首部字段"><a href="#非HTTP1-1首部字段" class="headerlink" title="非HTTP1.1首部字段"></a>非HTTP1.1首部字段</h3><p>Cookie，Set-Cookie，Content-Disposition</p>
<h1 id="发送多种数据的多部分对象集合"><a href="#发送多种数据的多部分对象集合" class="headerlink" title="发送多种数据的多部分对象集合"></a>发送多种数据的多部分对象集合</h1><ol>
<li><p>multipart/form-data<br>在Web表单文件上传时候使用</p>
</li>
<li><p>multipart/byteranges<br>状态码206响应报文包含了多个范围的内容时使用（后面介绍）；</p>
</li>
</ol>
<ul>
<li>在HTTP报文中使用多部分对象集合时，需要在首部字段里加上<strong>Content-type</strong>；</li>
<li>使用boundary字符串来划分多部分对象集合指明的各类实体。在boundary字符串制定的各个实体的起始行之前插入“–”标记（比如：–AaB03x、–THIS_STRING_SEPARATES–）作为起始。–AaB03x–、–THIS_STRING_SEPARATES–作为结束<br><img src="https://img-blog.csdnimg.cn/20200213212220545.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200213212233911.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></li>
<li>各部分对象集合的每个部分类型中，都可以含有首部字段；</li>
</ul>
<h1 id="获取部分内容的范围请求"><a href="#获取部分内容的范围请求" class="headerlink" title="获取部分内容的范围请求"></a>获取部分内容的范围请求</h1><p>执行范围请求时，会用到首部字段Range来指定资源的byte范围，指定方法：</p>
<ul>
<li>5001-10000字节</li>
</ul>
<blockquote>
<p>Range：bytes=5001-10000</p>
</blockquote>
<ul>
<li><p>5001之后全部</p>
<blockquote>
<p>Range：bytes=5001-</p>
</blockquote>
</li>
<li><p>从一开始到3000和5001到7000</p>
<blockquote>
<p>Range：bytes=0-3000,, 5000-7000</p>
</blockquote>
</li>
</ul>
<p>对于范围请求，响应会返回状态码为206的响应报文，对于多重范围请求响应会在首部字段添加Content-type：multipart/byteranges</p>
<h1 id="内容协商返回合适的内容"><a href="#内容协商返回合适的内容" class="headerlink" title="内容协商返回合适的内容"></a>内容协商返回合适的内容</h1><p>内容协商机制是指客户端和服务端就响应的资源内容进行交涉，然后提供给客户端最为合适的资源。内容包括：语言、字符集、编码方式等等…<br>三种内容协商技术：</p>
<ol>
<li>服务器驱动协商<br>以请求的首部字段作为参考，在服务器端自动处理</li>
<li>客户端驱动协商<br>用户手动选择显示的方式，可以利用javascript脚本在页面上进行上述选择。比如根据OS类型，自动切换PC版页面和手机版页面</li>
<li>透明协商<br>上述两者的结合</li>
</ol>
<p>通常通过下面这些字段作为判断</p>
<ul>
<li>Accept</li>
<li>Accept-Charset</li>
<li>Accept-Encoding</li>
<li>Accept-Language</li>
<li>Content-Language</li>
</ul>
<h1 id="Http的状态码"><a href="#Http的状态码" class="headerlink" title="Http的状态码"></a>Http的状态码</h1><p>状态码的职责是当客户端向服务端发送请求时，描述返回的请求结果。<br>状态码共分为下面这几类：</p>
<ol>
<li>1XX：信息性状态码——接收的请求正在处理</li>
<li>2XX：成功状态码——请求正常处理完毕</li>
<li>3XX：重定向状态码——需要进行附加操作才能完成请求</li>
<li>4XX：客户端错误状态码——服务器无法处理请求</li>
<li>5XX：服务器错误状态码——服务器处理请求出错</li>
</ol>
<h2 id="2XX——成功"><a href="#2XX——成功" class="headerlink" title="2XX——成功"></a>2XX——成功</h2><h3 id="200-OK"><a href="#200-OK" class="headerlink" title="200 OK"></a>200 OK</h3><p>从客户端发来的请求已经被正常处理了。一般用于GET和POST请求，对应请求资源的实体会作为响应返回</p>
<h3 id="202-No-Content"><a href="#202-No-Content" class="headerlink" title="202 No Content"></a>202 No Content</h3><p>服务器接收到请求已经成功处理，但返回的响应报文不含实体部分，也不允许返回任何实体。</p>
<h3 id="206-Partial-Content"><a href="#206-Partial-Content" class="headerlink" title="206 Partial Content"></a>206 Partial Content</h3><p>表示客户端进行了范围请求，而服务器成功执行了这部分的GET请求。</p>
<h2 id="3XX——重定向"><a href="#3XX——重定向" class="headerlink" title="3XX——重定向"></a>3XX——重定向</h2><h3 id="301-Moved-Permanently"><a href="#301-Moved-Permanently" class="headerlink" title="301 Moved Permanently"></a>301 Moved Permanently</h3><p>永久性重定向。表示请求的资源已经分配了新的URI，以后应该<strong>永远</strong>使用资源现在所指的URI。也就是说如果已经把资源对应的URI保存为书签了，这时应该按Location首部字段提示的URI<strong>重新保存</strong>。</p>
<p>使用首部字段location可以将响应接收方引导到某个与请求URI不同的资源。<br>几乎所有的浏览器在接收到包含location首部字段的响应后，都会强制性地尝试对已提示的重定向资源的访问</p>
<h3 id="302-Found"><a href="#302-Found" class="headerlink" title="302 Found"></a>302 Found</h3><p>临时重定向。表示请求的资源已经被分配了新的URI，希望用户<strong>本次</strong>使用新的URI访问。<br>这个状态码与301相似，但是302状态码代表的资源不是被永久移动的，这意味着对应资源的URI之后还有可能发生改变，所以客户端保存的URI<strong>不发生改变</strong></p>
<h3 id="303-See-Other"><a href="#303-See-Other" class="headerlink" title="303 See Other"></a>303 See Other</h3><p>表示由于请求的对应资源存在另一个URI，应使用<strong>GET</strong>方法定向获取请求资源。<br>303和302有着相同的功能，但303明确规定客户端应该采用GET方法获取资源，这点上与302有所区别。<br>当服务器希望客户端以GET方式重定向到另一个URI上时，返回303是一个比302更好的选择。</p>
<p>注意：现在几乎所有浏览器在遇到301,302,303时都会把POST改成GET，并删除请求报文的主体，之后再次发送。尽管302和303标准禁止这样做</p>
<h3 id="304-Not-Modified"><a href="#304-Not-Modified" class="headerlink" title="304 Not Modified"></a>304 Not Modified</h3><p>当客户端发送附带条件的请求（一般指采用GET方法请求的报文首部包含If-Match，If-Modified-Since，If-None-Match，If-Range， If-Unmodified-Since中任意首部字段），服务器允许访问资源，但是请求的条件未满足，则返回304<br>简单来说，就是比如客户端之前的请求已经缓存了资源，当客户端再次请求时，会提供一个头信息指出客户端希望只返回在指定日期之后修改的资源（客户端想要更新资源），服务器查看资源，发现资源并没有发生改变，现在客户端缓存的资源依然没有过期，可以使用，于是返回304。<br>304返回时<strong>不包含任何响应的主体</strong>。<br>304虽然为3XX，但是和重定向没有任何关系。</p>
<h3 id="307-Temporary-Redirect"><a href="#307-Temporary-Redirect" class="headerlink" title="307 Temporary Redirect"></a>307 Temporary Redirect</h3><p>临时重定向。这个状态码和302有相同的意义，只不过302标准禁止POST变为GET，但是307使用GET请求重定向</p>
<h2 id="4XX——客户端错误"><a href="#4XX——客户端错误" class="headerlink" title="4XX——客户端错误"></a>4XX——客户端错误</h2><h3 id="400-Bad-Request"><a href="#400-Bad-Request" class="headerlink" title="400 Bad Request"></a>400 Bad Request</h3><p>表示请求的报文当中存在语法错误。一般需要修改请求的内容之后再次发送请求。</p>
<h3 id="401-Unauthorized"><a href="#401-Unauthorized" class="headerlink" title="401 Unauthorized"></a>401 Unauthorized</h3><p>该状态码表示发送的请求需要有HTTP认证（BASIC，DIGEST），若之前已经进行过请求，则表示认证失败。一般返回401的响应必须有包含一个适用于被请求资源的WWW-Authenticate首部字段，用于告知用户使用的资源认证方案和带有参数的质询（challenge）</p>
<h3 id="403-Forbidden"><a href="#403-Forbidden" class="headerlink" title="403 Forbidden"></a>403 Forbidden</h3><p>该状态码表示访问的资源被服务器拒绝了。<br>未获取文件系统的访问权限，访问权限出现问题等情况都是403可能的原因。</p>
<h3 id="404-Not-Found"><a href="#404-Not-Found" class="headerlink" title="404 Not Found"></a>404 Not Found</h3><p>该状态码表示服务器上无法找到请求的资源</p>
<h2 id="5XX——服务端错误"><a href="#5XX——服务端错误" class="headerlink" title="5XX——服务端错误"></a>5XX——服务端错误</h2><h3 id="500-Internal-Server-Error"><a href="#500-Internal-Server-Error" class="headerlink" title="500 Internal Server Error"></a>500 Internal Server Error</h3><p>该状态码表示服务器在执行请求时发生了错误，很可能是web应用存在bug或一些临时性故障</p>
<h3 id="501-Not-Implemented"><a href="#501-Not-Implemented" class="headerlink" title="501    Not Implemented"></a>501    Not Implemented</h3><p>服务器不支持请求的功能，无法完成请求</p>
<h3 id="502-Bad-Gateway"><a href="#502-Bad-Gateway" class="headerlink" title="502    Bad Gateway"></a>502    Bad Gateway</h3><p>作为网关或者代理工作的服务器尝试执行请求时，从远程服务器接收到了一个无效的响应</p>
<h3 id="503-Service-Unavailable"><a href="#503-Service-Unavailable" class="headerlink" title="503 Service Unavailable"></a>503 Service Unavailable</h3><p>表示服务器目前处于超载状态或停机维护，现在无法处理请求。</p>
<h3 id="504-Gateway-Time-out"><a href="#504-Gateway-Time-out" class="headerlink" title="504    Gateway Time-out"></a>504    Gateway Time-out</h3><p>充当网关或代理的服务器，未及时从远端服务器获取请求</p>
<h3 id="505-HTTP-Version-not-supported"><a href="#505-HTTP-Version-not-supported" class="headerlink" title="505    HTTP Version not supported"></a>505    HTTP Version not supported</h3><p>服务器不支持请求的HTTP协议的版本，无法完成处理</p>
<h1 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h1><p>浏览器缓存是浏览器在本地磁盘对用户最近请求过的文档进行存储，当访问者再次访问同一页面时，浏览器就可以直接从本地磁盘加载文档。</p>
<p>在浏览器第一次发起请求时，本地无缓存，向web服务器发送请求，服务器起端响应请求，浏览器端缓存。<br>在第一次请求时，服务器会将页面最后修改时间通过Last-Modified标识由服务器发送给客户端，客户端记录修改时间；服务器还会生成一个Etag，并发送给客户端。<br><img src="https://img-blog.csdnimg.cn/20200215171453349.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h2 id="缓存的两种方式"><a href="#缓存的两种方式" class="headerlink" title="缓存的两种方式"></a>缓存的两种方式</h2><p>缓存分为两种：强缓存和协商缓存，根据响应的header内容来决定。<br><img src="https://img-blog.csdnimg.cn/20200215140252431.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<ul>
<li><p>浏览器请求某一资源时，会先获取该资源缓存的header信息，然后根据header中的Cache-Control和Expires来判断是否过期。<strong>若没过期则直接从缓存中获取资源信息</strong>，包括缓存的header的信息，所以此次请求不会与服务器进行通信。这里判断是否过期，则是强缓存相关。后面会讲Cache-Control和Expires相关。</p>
</li>
<li><p><strong>如果显示已过期，浏览器会向服务器端发送请求</strong>，这个请求会携带第一次请求返回的有关缓存的header字段信息，比如客户端会通过If-None-Match头将先前服务器端发送过来的Etag发送给服务器，服务会对比这个客户端发过来的Etag是否与服务器的相同，若相同，就将If-None-Match的值设为false，返回状态304，客户端继续使用本地缓存，不解析服务器端发回来的数据，若不相同就将If-None-Match的值设为true，返回状态为200，客户端重新机械服务器端返回的数据；客户端还会通过If-Modified-Since头将先前服务器端发过来的最后修改时间戳发送给服务器，服务器端通过这个时间戳判断客户端的页面是否是最新的，如果不是最新的，则返回最新的内容，如果是最新的，则返回304，客户端继续使用本地缓存。</p>
</li>
<li><h2 id="强缓存"><a href="#强缓存" class="headerlink" title="强缓存"></a>强缓存</h2>强缓存是利用http头中的<strong>Expires和Cache-Control</strong>两个字段来控制的，用来表示资源的缓存时间。</li>
</ul>
<p>强缓存中，普通刷新会忽略它（点击浏览器的刷新就走协商缓存，不用强缓存），但不会清除它，需要强制刷新。浏览器强制刷新，请求会带上Cache-Control:no-cache和Pragma:no-cache</p>
<h3 id="Expires"><a href="#Expires" class="headerlink" title="Expires"></a>Expires</h3><p>Expires是http1.0的规范，它的值是一个绝对时间的GMT格式的时间字符串。例如：expires:Fri, 14 Apr 2017 10:47:02 GMT。这个时间代表这这个资源的失效时间，只要发送请求时间是在Expires之前，那么本地缓存始终有效，则在缓存中读取数据。所以这种方式有一个明显的缺点，由于失效的时间是一个绝对时间，所以当服务器与客户端时间偏差较大时，就会导致缓存混乱。如果同时出现Cache-Control:max-age和Expires，那么max-age优先级更高</p>
<h3 id="Cache-Control"><a href="#Cache-Control" class="headerlink" title="Cache-Control"></a>Cache-Control</h3><p>Cache-Control是在http1.1中出现的，主要是利用该字段的max-age值来进行判断，它是一个相对时间，例如Cache-Control:max-age=3600，代表着资源的有效期是3600秒。cache-control除了该字段外，还有下面几个比较常用的设置值：</p>
<ul>
<li><p>no-cache：不使用本地缓存。需要使用缓存协商，先与服务器确认返回的响应是否被更改，如果之前的响应中存在ETag，那么请求的时候会与服务端验证，如果资源未被更改，则可以避免重新下载。</p>
</li>
<li><p>no-store：直接禁止游览器缓存数据，每次用户请求该资源，都会向服务器发送一个请求，每次都会下载完整的资源。</p>
</li>
<li><p>public：可以被所有的用户缓存，包括终端用户和CDN等中间代理服务器。</p>
</li>
<li><p>private：只能被终端用户的浏览器缓存，不允许CDN等中继缓存服务器对其缓存。<br>Cache-Control与Expires可以在服务端配置同时启用，同时启用的时候Cache-Control优先级高。</p>
</li>
</ul>
<h2 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h2><p>协商缓存就是由服务器来确定缓存资源是否可用，所以客户端与服务器端要通过某种标识来进行通信，从而让服务器判断请求资源是否可以缓存访问。</p>
<p><strong>普通刷新会启用弱缓存，忽略强缓存。只有在地址栏或收藏夹输入网址、通过链接引用资源等情况下，浏览器才会启用强缓存</strong>，这也是为什么有时候我们更新一张图片、一个js文件，页面内容依然是旧的，但是直接浏览器访问那个图片或文件，看到的内容却是新的。</p>
<p>这个主要涉及到两组header字段：<strong>Etag和If-None-Match、Last-Modified和If-Modified-Since</strong></p>
<h3 id="Etag和If-None-Match"><a href="#Etag和If-None-Match" class="headerlink" title="Etag和If-None-Match"></a>Etag和If-None-Match</h3><p>Etag/If-None-Match返回的是一个校验码。ETag可以保证每一个资源是唯一的，资源变化都会导致ETag变化。服务器根据浏览器上送的If-None-Match值来判断是否命中缓存。</p>
<p>与Last-Modified不一样的是，当服务器返回304 Not Modified的响应时，由于ETag重新生成过，response header中还会把这个ETag返回，即使这个ETag跟之前的没有变化。</p>
<h3 id="Last-Modify-If-Modify-Since"><a href="#Last-Modify-If-Modify-Since" class="headerlink" title="Last-Modify/If-Modify-Since"></a>Last-Modify/If-Modify-Since</h3><p>浏览器第一次请求一个资源的时候，服务器返回的header中会加上Last-Modify，Last-modify是一个时间标识该资源的最后修改时间，例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。</p>
<p>当浏览器再次请求该资源时，request的请求头中会包含If-Modify-Since，该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后，根据资源的最后修改时间判断是否命中缓存。</p>
<p>如果命中缓存，则返回304，并且不会返回资源内容，并且不会返回Last-Modify。</p>
<p>Last-Modified与ETag是可以一起使用的，服务器会优先验证ETag，一致的情况下，才会继续比对Last-Modified，最后才决定是否返回304。</p>
<h1 id="通信数据转发程序"><a href="#通信数据转发程序" class="headerlink" title="通信数据转发程序"></a>通信数据转发程序</h1><h2 id="代理"><a href="#代理" class="headerlink" title="代理"></a>代理</h2><p>接收客户端发送的请求后转发给服务器，代理<strong>不改变请求的URI</strong>，会直接发送给前方持有此资源的目标服务器。<br>使用代理服务器一般可以：</p>
<ol>
<li>利用缓存技术，减少网络流量</li>
<li>组织内部针对特定的网站进行访问控制，以获取访问日志为主要的目的</li>
</ol>
<p>具体而言，<strong>代理服务器可以分为正向代理和反向代理服务器</strong>。<strong>代理服务又分为缓存代理和透明代理</strong></p>
<h3 id="正向代理"><a href="#正向代理" class="headerlink" title="正向代理"></a>正向代理</h3><p>在正向代理的过程中，隐藏了真实的客户端，即服务器端不知道客户端是谁。某些科学上网工具扮演的就是正向代理服务器：比如用浏览器访问：”<a href="http://www.google.com&quot;时被限制访问，你就可以在国外搭建一台代理服务器，通过这台代理服务器去请求www.google.com,代理服务器再把返回的响应内容返回给你。" target="_blank" rel="noopener">http://www.google.com&quot;时被限制访问，你就可以在国外搭建一台代理服务器，通过这台代理服务器去请求www.google.com,代理服务器再把返回的响应内容返回给你。</a></p>
<h3 id="反向代理"><a href="#反向代理" class="headerlink" title="反向代理"></a>反向代理</h3><p>反向代理过程中隐藏了真实服务器的信息，用户不需要知道是哪台服务器返回的信息，只要知道反向代理服务器是谁就行了。比如：用上文在国外搭建的代理服务器A访问:<a href="http://www.google.com，这里的A就相当于是一个客户端,www.google.com就是我们的反向代理服务器，反向代理服务器会把我们的请求转发给真实服务器，由真实服务器处理请求并返回资源到反向代理服务器，反向代理服务器再把资源转发给A。所以在整个过程中A并不知道是哪台服务器处理了请求，这样就很好的隐藏了真实服务器。" target="_blank" rel="noopener">http://www.google.com，这里的A就相当于是一个客户端,www.google.com就是我们的反向代理服务器，反向代理服务器会把我们的请求转发给真实服务器，由真实服务器处理请求并返回资源到反向代理服务器，反向代理服务器再把资源转发给A。所以在整个过程中A并不知道是哪台服务器处理了请求，这样就很好的隐藏了真实服务器。</a></p>
<h3 id="缓存代理"><a href="#缓存代理" class="headerlink" title="缓存代理"></a>缓存代理</h3><p>预先将资源的副本缓存在代理服务器上，当代理再次接受到相同的资源请求，就可以使用缓存资源作为响应返回</p>
<h3 id="透明代理"><a href="#透明代理" class="headerlink" title="透明代理"></a>透明代理</h3><p>转发请求时不对报文进行任何修改和加工</p>
<h2 id="网关"><a href="#网关" class="headerlink" title="网关"></a>网关</h2><p><img src="https://img-blog.csdnimg.cn/2020021511140194.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>网关的的工作机制和代理十分相似，但网关有一些自己的特点</p>
<ol>
<li>网关可以把http请求转换为其他协议的请求，提供非HTTP协议服务，比如可以链接数据库等，提高系统的的安全性</li>
<li>网关是转发其他服务器通信数据的服务器，就像自己拥有资源的源服务器一样对请求进行处理的</li>
</ol>
<h2 id="隧道"><a href="#隧道" class="headerlink" title="隧道"></a>隧道</h2><p>建立安全的通信线路，<strong>配合SSL加密等手段进行安全通信</strong>，本身不解析http请求，保持原样中转给之后的服务器。<br>好处：可以安全和远距离服务器安全通信，隧道本身透明，客户端不用在意隧道的存在</p>
<h1 id="HTTPS"><a href="#HTTPS" class="headerlink" title="HTTPS"></a>HTTPS</h1><h2 id="https和http"><a href="#https和http" class="headerlink" title="https和http"></a>https和http</h2><p>http:: 超文本传输协议，是互联网上应用最为广泛的一种网络协议，是一个客户端和服务器端请求和应答的标准（TCP），用于从WWW服务器传输超文本到本地浏览器的传输协议，它可以使浏览器更加高效，使网络传输减少。<br>https: 是以安全为目标的HTTP通道，简单讲是HTTP的安全版，即HTTP下加入SSL层，HTTPS的安全基础是SSL，因此加密的详细内容就需要SSL。</p>
<h2 id="http的缺陷"><a href="#http的缺陷" class="headerlink" title="http的缺陷"></a>http的缺陷</h2><ol>
<li>通信过程使用明文，内容可能会被窃听</li>
<li>不验证通信方身份，因此有可能遭遇伪装</li>
<li>无法证明报文完整性，可能遭到篡改</li>
</ol>
<h2 id="两者的区别"><a href="#两者的区别" class="headerlink" title="两者的区别"></a>两者的区别</h2><ol>
<li>Https协议需要ca证书，费用较高，用于验证身份</li>
<li>http是超文本传输协议，信息是明文传输，https则是具有安全性的ssl加密传输协议。</li>
<li>使用不同的链接方式，端口也不同，一般而言，http协议的端口为80，https的端口为443</li>
<li>http的连接很简单，是无状态的；HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议，比http协议安全。</li>
</ol>
<h2 id="https：加密、认证、完整性保护"><a href="#https：加密、认证、完整性保护" class="headerlink" title="https：加密、认证、完整性保护"></a>https：加密、认证、完整性保护</h2><p>https就是身披SSL外衣的http</p>
<h3 id="SSL加密方式：密钥"><a href="#SSL加密方式：密钥" class="headerlink" title="SSL加密方式：密钥"></a>SSL加密方式：密钥</h3><h4 id="共享密钥加密"><a href="#共享密钥加密" class="headerlink" title="共享密钥加密"></a>共享密钥加密</h4><p>加密和解密共用一个密钥的方式称为共享密钥加密，也叫对称密钥加密。<br>以共享密钥的方式加密时就必须把密钥也发送给对方，这样密钥就会有泄露的风险，密钥泄露那么加密也就没有了意义</p>
<h4 id="公开密钥加密"><a href="#公开密钥加密" class="headerlink" title="公开密钥加密"></a>公开密钥加密</h4><p>这种方式使用一对<strong>非对称密钥</strong>，一把私有密钥，一把公开密钥，发送密文的一方使用对方提供的公开密钥加密，对方收到密文之后在使用私有密钥解密。<br>这种方式下只知道公开密钥和密文，想要还原信息原文是几乎不可能的。<br>同时，这种方式下客户端收到公开密钥之后，需要使用证书来验证其真实性</p>
<h4 id="https使用混合加密"><a href="#https使用混合加密" class="headerlink" title="https使用混合加密"></a>https使用混合加密</h4><p>由于公开密钥加密相对于共享密钥，处理速度慢，因此https采用共享密钥和公开密钥两者混合的方式进行加密。在确保安全的情况下使用共享密钥</p>
<h2 id="https通信机制"><a href="#https通信机制" class="headerlink" title="https通信机制"></a>https通信机制</h2><ol>
<li>客户端发送ClientHello报文开始SSL通信，报文中包含客户端指定的SSL版本，加密组件，列表（所使用的加密算法和密钥长度等）</li>
<li>服务端可以进行SSL通信，返回一个ServerHello报文，包含SSL版本，加密组件（服务端返回的加密组件是从请求报文当中筛选的）</li>
<li>服务器发送Certificate报文，包含公开密钥和证书</li>
<li>服务端发送ServerHelloDone报文通知客户端，最初阶段的SSL握手协商部分结束</li>
</ol>
<p>客户端随机生成Pre-master Secret（主要是通过RSA或者Diffie-Hellman算法生成）放入Client Key Exchange报文中。</p>
<p>使用证书携带的公开密钥对Client Key Exchange报文加密处理。</p>
<p>用Pre-master secret生成 master secre；还有生成CBC模式的初始向量（CBC模式第一次明文块做XOR的时候需要用到的初始向量，只要选择不同的初始向量，相同的密文加密后会形成不同的密文，这是目前应用最广泛的模式。CBC加密后的密文是上下文相关的，但如果一个分组丢失，后面的分组将全部作废，即同步错误）。</p>
<ol start="5">
<li>SSL握手第一次结束之后，验证公密有效性。客户端以ClientKeyExchange报文作为回应，报文中包含一种被称为Pre-master secret的随机密码串，报文已经使用3中的密钥加密</li>
<li>接着客户端继续发送ChangeCipherSpec报文，提示服务器此报文之后的通信使用Pre-master secret密钥加密</li>
<li>客户端发送Finished报文，包含连接至今全部报文的整体校验值。这次握手能否成功取决于服务器能否正确解密该报文</li>
<li>服务器发送ChangeCipherSpec报文</li>
<li>服务器发送Finished报文</li>
<li>服务器和客户端的Finished报文交换完毕，SSL连接建立完成，通信受到SSL保护，从此处开始http请求<ol start="11">
<li>开始通信</li>
<li>最后由客户端断开连接</li>
</ol>
</li>
</ol>
<p><img src="https://img-blog.csdnimg.cn/2020021513121725.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwMzE5NTg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>CBC模式(Cipher Block Chaining) —— 又名密码<strong>分组链接模式</strong>。在此模式下，将前一个明文块加密处理后和下一个明文块做<strong>XOR运算</strong>，使之重叠，然后再对运算结果做加密处理。对第一个明文块做加密时，要么使用<strong>前一段密文的最后一块</strong>，要么利用外部生成的<strong>初始向量</strong></p>
<h2 id="http和https的选择"><a href="#http和https的选择" class="headerlink" title="http和https的选择"></a>http和https的选择</h2><p>与纯文本通信相比，加密通信会耗费更多的CPU和内存。所以非敏感信息使用http，敏感信息使用https<br>除此之外，节约一部分购买证书的开销也是原因之一</p>
<h1 id="使用浏览器进行全双工通信的WebSocket"><a href="#使用浏览器进行全双工通信的WebSocket" class="headerlink" title="使用浏览器进行全双工通信的WebSocket"></a>使用浏览器进行全双工通信的WebSocket</h1><h2 id="WebSocket协议"><a href="#WebSocket协议" class="headerlink" title="WebSocket协议"></a>WebSocket协议</h2><p>这是一个<strong>建立在HTTP基础上</strong>的协议，因此连接的发起方依然是客户端，一旦建立了websocket连接，无论是客户端还是服务端都可以像对方发送信息。<br>通信过程可以使用JSON，XML，HTML，图片等任意格式的数据</p>
<h2 id="WebSocket特点"><a href="#WebSocket特点" class="headerlink" title="WebSocket特点"></a>WebSocket特点</h2><ol>
<li>推送功能<br>支持由服务器向客户端推送数据的功能，这样服务器可以直接发送数据而不用等待客户端的请求</li>
<li>减少通信量<br>只要建立起WebSocket连接，就希望一直保持连接状态，这样每次连接的开销就减少了<h2 id="WebSocket通信步骤"><a href="#WebSocket通信步骤" class="headerlink" title="WebSocket通信步骤"></a>WebSocket通信步骤</h2><h3 id="握手-请求"><a href="#握手-请求" class="headerlink" title="握手-请求"></a>握手-请求</h3>为了实现WebSocket通信吗，需要用到HTTP报文的Upgrade字段，告知服务器通信协议发生改变，已达到握手的目的</li>
</ol>
<blockquote>
<p>GET /chat HTTP/1.1<br>Host: server.example.com<br><strong>Upgrade: websocket<br>Connection: Upgrade</strong><br>Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==<br>Sec-WebSocket-Protocol: chat, superchat<br>Sec-WebSocket-Version: 13<br>Origin: <a href="http://example.com" target="_blank" rel="noopener">http://example.com</a></p>
</blockquote>
<p>Sec-WebSocket-Key字段记录着握手过程不可少的键值<br>Sec-WebSocket-Protocol字段记录使用的子协议<br>以及Sec-WebSocket-Version</p>
<h3 id="握手-响应"><a href="#握手-响应" class="headerlink" title="握手 - 响应"></a>握手 - 响应</h3><p>对于之前的请求，返回状态码101的响应<br>Sec-WebSocket-Accept字段由握手请求中的Sec-WebSocket-Key字段生成</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>Copyright： </strong>
          Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2020/02/26/Event-Loop/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            Event-Loop
          
        </div>
      </a>
    
    
      <a href="/2020/02/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">面向对象的程序设计</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "2eBxdPB3eEwraN7IpwFckzeL-gzGzoHsz",
    app_key: "VGgCefQEluVxHdyMsm1Dpeih",
    path: window.location.pathname,
    avatar: "monsterid",
    placeholder: "评论一下我的文章吧~",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
     
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2020
        <i class="ri-heart-fill heart_icon"></i> AURORA_ZXH
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        Powered by <a href="https://hexo.io" target="_blank">Hexo</a>
        <span class="division">|</span>
        Theme - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>Visitors:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>Views:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/avatar.jpg" alt="AURORA"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="http://aurora-zxh.lofter.com/" target="_blank" rel="noopener">随记</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about">关于我</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/link">友链</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.png">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/wxpay.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
  </div>
</body>

</html>